import shutil
from pprint import pprint
from uuid import uuid4

import arrow
import pexpect
from starlette.responses import RedirectResponse

from core import utils
from core.repositories._base import CRUDRepository
from core.services import get_db, get_vm_conn, db
from core.validators._base import BaseValidator
from eve.configs.app import APP_EVE_PATH
from eve.exceptions._all import VirtualMachineException
from eve.models.virtual_machine_instance import LibvirtDomain, VirtualMachineInstance
from eve.models.libvirt_models.domain import Domain, ini_domain
from eve.models.virtual_macihne_template import VirtualMachineTemplate
from eve.validators.virtual_machine_instance import (
    VirtualMachineInstanceCreatingValidator as CreatingValidator,
    VirtualMachineInstantiatingValidator as InstantiatingValidator
)


class VirtualMachineInstanceRepository(CRUDRepository):
    def _build_domain_xml(self, domain_name, memory, cpu_num, os_arch_type, qcow2_path=None):
        # atodo: 建 interface_num 个网卡

        def _build_memory(memory, maxMemory=None, currentMemory=None):
            s = ''
            if maxMemory: s += f'''<maxMemory slots='16' unit='MB'>{maxMemory}</maxMemory>'''
            s += f'''<memory unit='MB'>{memory}</memory>'''
            if currentMemory: s += f'''<currentMemory unit='KiB'>{currentMemory}</currentMemory>'''
            return s

        if qcow2_path is None: qcow2_path = str(APP_EVE_PATH/'qcow2_templates/CentOS-7-x86_64-GenericCloud-2009.qcow2')

        arch = LibvirtDomain.OS_ARCH_TYPE(os_arch_type).name
        return f'''
<domain type='kvm'>
    <name>{domain_name}</name>
    <memory unit='MB'>{memory}</memory>
    <vcpu placement='static'>{cpu_num}</vcpu>
    <os>
        <type arch='{arch}' machine='pc-i440fx-rhel7.0.0'>hvm</type>
        <boot dev='hd'/>
    </os>
    <features>
        <acpi/>
        <apic/>
    </features>
    <on_poweroff>destroy</on_poweroff>
    <on_reboot>destroy</on_reboot>
    <on_crash>destroy</on_crash>
    <pm>
        <suspend-to-mem enabled='no'/>
        <suspend-to-disk enabled='no'/>
    </pm>
    <devices>
        <emulator>/usr/libexec/qemu-kvm</emulator>
        <disk type='file' device='disk'>
            <driver name='qemu' type='qcow2'/>
            <source file='{qcow2_path}'/>
            <target dev='hda' bus='ide'/>
        </disk>
        <interface type='network'>
            <source network='default' bridge='virbr0'/>
        </interface>
        <console type='pty'>
            <target type='serial'/>
        </console>
    </devices>
</domain>
                '''

    def create(self, payload: CreatingValidator):
        domain_name = str(uuid4())

        db.begin_transaction()

        libvirt_domain_dict = payload.libvirt_domain.dict()
        libvirt_domain_dict['name'] = domain_name
        libvirt_domain = LibvirtDomain(libvirt_domain_dict)
        libvirt_domain.save()

        virtual_machine_instance_dict = payload.dict()
        del virtual_machine_instance_dict['libvirt_domain']
        virtual_machine_instance_dict['libvirt_domain_id'] = libvirt_domain.id

        virtual_machine_instance = VirtualMachineInstance(virtual_machine_instance_dict)
        virtual_machine_instance.save()

        conn = get_vm_conn()
        vm_conn = next(conn)

        ld = payload.libvirt_domain
        domain_xml = self._build_domain_xml(domain_name, ld.memory, ld.cpu_num, ld.os_arch_type)
        domain: Domain = Domain.from_xml(domain_xml)
        path = domain.devices.disk.value__
        src = path.split('.')[0]+'.qcow2'
        # 从 vm_template 取出 qcow2_template_name
        # APP_EVE_PATH / 'templates' / qcow2_template_name 复制到
        # APP_EVE_PATH / 'instances' / domain_name+'.qcow2'
        dst = path.split('.')[0]+'-'+domain_name+'.qcow2'

        try:
            shutil.copy(src, dst)
            domain.devices.disk.value__ = dst
            domain_xml = domain.to_xml()
            vm_conn.defineXML(domain_xml)
        except Exception as e:
            db.rollback()
            raise e
            raise VirtualMachineException("创建虚拟机失败")

        db.commit()
        return virtual_machine_instance

    def boot_domain(self, id: int):
        virtual_machine_instance = self.Model.find_or_fail(id)
        db.begin_transaction()
        try:
            domain_name = virtual_machine_instance.libvirt_domain.name
            client = get_vm_conn()
            conn = next(client)
            virDomain = conn.lookupByName(domain_name)
            r = virDomain.create()
            # child = pexpect.spawn(f'virsh start {domain_name}')
            # print(child.readline())
            # child.close()

            virtual_machine_instance.status = VirtualMachineInstance.STAUS.powered_on.value
            virtual_machine_instance.save()

        except Exception as e:
            db.rollback()
            raise e

        db.commit()
        # print(child.readline())

        # conn = get_vm_conn()
        # vm_conn = next(conn)
        # try:
        #     domain = vm_conn.lookupByName()
        # except Exception as e:
        #     raise VirtualMachineException("设备名称不存在")  # 定义自己的错误类型
        # xmlconfig = domain.XMLDesc()
        # # dom = None
        # try:
        #     flag = vm_conn.createXML(xmlconfig, 0)  # 启动成功过是1，失败为0
        # except Exception as e:
        #     raise VirtualMachineException("启动设备失败")
        return virtual_machine_instance

    # destroy domain
    def destroy_domain(self, id: int):
        client = get_db()
        db = next(client)
        virtual_machine_instance: VirtualMachineInstance = db.query(VirtualMachineInstance).get(id)
        if not virtual_machine_instance: raise VirtualMachineException('虚拟机不存在')

        domain_name = virtual_machine_instance.libvirt_domain.name

        conn = get_vm_conn()
        vm_conn = next(conn)
        try:
            domain = vm_conn.lookupByName(domain_name)
        except Exception as e:
            raise VirtualMachineException("设备名称不存在")
        try:
            flag = domain.destroy()  # 销毁成功是1，失败是0
        except Exception as e:
            raise VirtualMachineException("销毁设备失败")
        return {"flag": flag}

    # shutdown domain
    def shutdown_domain(self, id: int):
        virtual_machine_instance = self.Model.find_or_fail(id)
        db.begin_transaction()
        try:
            domain_name = virtual_machine_instance.libvirt_domain.name
            client = get_vm_conn()
            conn = next(client)
            virDomain = conn.lookupByName(domain_name)
            r = virDomain.shutdown()
            # child = pexpect.spawn(f'virsh start {domain_name}')
            # print(child.readline())
            # child.close()

            virtual_machine_instance.status = VirtualMachineInstance.STAUS.powered_off.value
            virtual_machine_instance.save()

        except Exception as e:
            db.rollback()
            raise e

        db.commit()

        return virtual_machine_instance

    # Determine if the Domain is running
    def check_status(self, id: int):
        name = self._get_domain_name(id)
        conn = get_vm_conn()
        vm_conn = next(conn)
        try:
            domain = vm_conn.lookupByName(name)
        except Exception as e:
            raise VirtualMachineException("设备名称不存在")
        flag = domain.isActive()  # running = 1/shutoff = 0
        return {"flag": flag}

    def enter_console(self, id: int):
        domain_name = 'centos7.0'
        domain_name = self.Model.find_or_fail(id).libvirt_domain.name
        url = f'http://eve.aponder.top:33005/?arg={domain_name}'
        return RedirectResponse(url=url)

    def instantiate(self, payload: InstantiatingValidator):
        domain_name = str(uuid4())

        db.begin_transaction()

        domain: Domain = ini_domain
        domain.name.value__ = domain_name
        virtual_machine_template = VirtualMachineTemplate.find_or_fail(payload.virtual_machine_template_id)
        domain.memory.value__ = virtual_machine_template.ram
        qcow2 = virtual_machine_template.qcow2

        virtual_machine_instance_dict = payload.dict()
        virtual_machine_instance_dict['domain_name'] = domain_name
        virtual_machine_instance_dict['cpu'] = virtual_machine_template.cpu
        virtual_machine_instance_dict['ram'] = virtual_machine_template.ram
        # virtual_machine_instance_dict['qcow2'] = qcow2
        virtual_machine_instance = VirtualMachineInstance(virtual_machine_instance_dict)
        virtual_machine_instance.save()

        conn = get_vm_conn()
        vm_conn = next(conn)

        src = APP_EVE_PATH / 'templates' / qcow2
        # 从 vm_template 取出 qcow2
        dst = str(APP_EVE_PATH / 'instances' / domain_name) + '.qcow2'

        try:
            shutil.copy(src, dst)
            domain.devices.disk.value__ = dst
            domain_xml = domain.to_xml()
            vm_conn.defineXML(domain_xml)
        except Exception as e:
            db.rollback()
            raise e
            raise VirtualMachineException("创建虚拟机失败")

        db.commit()
        return virtual_machine_instance

